package ee.ttu.agent.smartHouse.services;

import jade.core.behaviours.FSMBehaviour;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.WakerBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;

import java.util.concurrent.atomic.AtomicInteger;

import ee.ttu.agent.smartHouse.agent.FacilitatorAgent;
import ee.ttu.agent.smartHouse.util.Constants;
import ee.ttu.agent.smartHouse.util.MessageTemplateBuilder;

@SuppressWarnings("serial")
public class DorOpenerBehavior extends FSMBehaviour {
	private FacilitatorAgent userAgent;

	public DorOpenerBehavior(FacilitatorAgent userAgent) {
		AtomicInteger messagesSentToSupervisor = new AtomicInteger();
		AtomicInteger messagesSentToFacilitator = new AtomicInteger();
		this.userAgent = userAgent;
		
		registerFirstState(new InnerAuthorizationCheck(), "INIT_STATE");
		
		registerState(new RequestSender(Constants.AUTHORIZATION_REQUEST, Constants.AUTHORIZATION_SERVICE, messagesSentToSupervisor), "REQUEST_SV");
		registerState(new SuperWizorResponse(), "RESPONSE_SV");
		registerState(new RequestSender(Constants.AUTHORIZATION_CHECK_REQUEST, Constants.AUTHORIZATION_CHECK_SERVICE, messagesSentToFacilitator), "REQUEST_FACILITATOR");
		registerState(new FaclilitatorResponse(messagesSentToFacilitator), "RESPONSE_FACILITATOR");

		registerLastState(new OpenDor(false), "OPEN_DOR");
		registerLastState(new OpenDor(true), "OPEN_DOR_R");
		registerLastState(new CloseDor(), "CLOSE_DOR");
		registerLastState(new OnHoldDor(), "ON_HOLD_DOR");
		
		
		registerDefaultTransition("REQUEST_FACILITATOR", "RESPONSE_FACILITATOR");
		registerDefaultTransition("REQUEST_SV", "RESPONSE_SV");
		
		registerTransition("INIT_STATE", "OPEN_DOR", 1);
		registerTransition("INIT_STATE", "REQUEST_FACILITATOR", 0);

		registerTransition("RESPONSE_FACILITATOR", "OPEN_DOR_R", 1);
		registerTransition("RESPONSE_FACILITATOR", "REQUEST_SV", 0);

		registerTransition("RESPONSE_SV", "CLOSE_DOR", 0);
		registerTransition("RESPONSE_SV", "OPEN_DOR", 1);
		registerTransition("RESPONSE_SV", "OPEN_DOR_R", 2);
		registerTransition("RESPONSE_SV", "ON_HOLD_DOR", 3);
	}

	private class InnerAuthorizationCheck extends OneShotBehaviour{
		private int result = 0;
		
		@Override
		public void action() {
			if(userAgent.getRegisteredUsers().containsKey(userAgent.getAuthCode()))
				result = 1;
			else
				result = 0;
		}
		
		@Override
		public int onEnd() {
			return result;
		}
	}
	
	private class RequestSender extends OneShotBehaviour{
		
		private String ontology;
		private String serviceType;
		private AtomicInteger messagesSent;

		public RequestSender(String ontology, String serviceType, AtomicInteger messagesSent) {
			this.ontology = ontology;
			this.serviceType = serviceType;
			this.messagesSent = messagesSent;
		}
		
		@Override
		public void onStart() {
			messagesSent.set(0);
		}
		
		@Override
		public void action() {
			ServiceDescription sd = new ServiceDescription();
			sd.setType(serviceType);
			DFAgentDescription dfAgentDescription = new DFAgentDescription();
			dfAgentDescription.addServices(sd);
			try {
				DFAgentDescription[] agents = DFService.search(userAgent, dfAgentDescription);
				for (DFAgentDescription agent : agents) {
					if(agent.getName().equals(userAgent.getName()))
						continue;
					ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
					msg.addReceiver(agent.getName());
					msg.setLanguage("English");
					msg.setOntology(ontology);
					msg.setSender(userAgent.getAID());
					msg.setContent(userAgent.getAuthCode());
					System.out.println("Sending" + msg);
					userAgent.send(msg);
					messagesSent.incrementAndGet();
				}
			} catch (FIPAException e) {
				e.printStackTrace();
			}
		}
	}
	
	public class SuperWizorResponse extends OneShotBehaviour {
		private int result;

		@Override
		public void action() {
			ACLMessage msg;
			while((msg= myAgent.receive(MessageTemplateBuilder.buildTemplate(ACLMessage.INFORM, Constants.AUTHORIZATION_RESPONSE)))==null)
				block();
			String response = msg.getContent();
			switch (response) {
			case Constants.ALLOW:
				result = 1;
				break;
			case Constants.ALLOW_PERM:
				result = 2;
				break;
			case Constants.ONHOLD:
				result = 3;
				break;
			default:
				result = 0;
				break;
			}
		}
		
		@Override
		public int onEnd() {
			return result;
		}
	}	
	public class FaclilitatorResponse extends OneShotBehaviour {
		private int result;
		AtomicInteger messagesSent;
		
		public FaclilitatorResponse(AtomicInteger messagesSent) {
			this.messagesSent = messagesSent;
		}
		
		@Override
		public void onStart() {
			result = 0;
		}
		
		@Override
		public void action() {
			for (int i = 0; i < messagesSent.get(); i++) {
				ACLMessage msg;
				while((msg= myAgent.receive(MessageTemplateBuilder.buildTemplate(ACLMessage.INFORM, Constants.AUTHORIZATION_CHECK_RESPONSE)))==null)
					block();
				String response = msg.getContent();
				switch (response) {
				case Constants.ALLOW:
					result = 1;
					break;
				}
			}
		}
		
		@Override
		public int onEnd() {
			return result;
		}
	}	
	
	private class CloseDor extends OneShotBehaviour{
		
		@Override
		public void action() {
			userAgent.closeDor();
		}
	}
	
private class OnHoldDor extends OneShotBehaviour{
		
		@Override
		public void action() {
			userAgent.onHoldDor();
		}
	}
	
	private class OpenDor extends WakerBehaviour{
		private boolean remember;

		public OpenDor(boolean remember) {
			super(userAgent, 2000);
			this.remember = remember;
		}
		
		@Override
		public void onStart() {
			userAgent.openDor(remember);
			System.out.println("DorOpener.OpenDor.onStart("+ remember +")");
		}
		
		@Override
		protected void onWake() {
			userAgent.closeDor();
		}
	}
}
